/*
 * Author: yezi
 * E-mail: okye328@gmail.com
 * Create time: Dec 22, 2012 11:19:48 PM
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 */

package com.kanbox.linux.token;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONObject;
import com.kanbox.linux.GlobalConf;

public class TokenManager {

	private Logger logger = LoggerFactory.getLogger(TokenManager.class);

	private HttpClient client = new DefaultHttpClient();

	private Token token = new Token();

	private static TokenManager THE_TOKEN_MANAGER = new TokenManager();

	private TokenManager() {
		if (!token.readFromFile()) {
			initToken();
		} else {
			if (token.getAccess_token() == null) {
				initToken();
			} else {
				refreshToken();
			}
		}
		Thread thread = new Thread(new Runnable() {
			@Override
			public void run() {
				while (true) {
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						logger.error("Thread interrupted unexpectly", e);
					}
					if (System.currentTimeMillis() - token.getLast_refresh() > (token
							.getExpire_time() * 1000 * 2 / 3)) {
						refreshToken();
					}
				}
			}
		});
		thread.setDaemon(true);
		thread.start();
	}

	private HttpPost buildGetTokenHTTPPost(String authCode) {
		List<NameValuePair> formparams = new ArrayList<NameValuePair>();
		formparams.add(new BasicNameValuePair("grant_type",
				"authorization_code"));
		formparams
				.add(new BasicNameValuePair("client_id", GlobalConf.CLIENT_ID));
		formparams.add(new BasicNameValuePair("client_secret",
				GlobalConf.CLIENT_SECRET));
		formparams.add(new BasicNameValuePair("redirect_uri",
				GlobalConf.NATIVE_REDIRECT_URL));
		formparams.add(new BasicNameValuePair("code", authCode));
		UrlEncodedFormEntity entity = null;
		try {
			entity = new UrlEncodedFormEntity(formparams, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			logger.error("Encode not support", e);
		}

		HttpPost post = new HttpPost("https://auth.kanbox.com/0/token");
		post.setEntity(entity);

		return post;
	}

	private HttpPost buildRefreshTokenHTTPPost(String refreshToken) {
		List<NameValuePair> formparams = new ArrayList<NameValuePair>();
		formparams.add(new BasicNameValuePair("grant_type", "refresh_token"));
		formparams
				.add(new BasicNameValuePair("client_id", GlobalConf.CLIENT_ID));
		formparams.add(new BasicNameValuePair("client_secret",
				GlobalConf.CLIENT_SECRET));

		formparams.add(new BasicNameValuePair("refresh_token", refreshToken));
		UrlEncodedFormEntity entity = null;
		try {
			entity = new UrlEncodedFormEntity(formparams, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			logger.error("Encode not support", e);
		}

		HttpPost post = new HttpPost("https://auth.kanbox.com/0/token");
		post.setEntity(entity);

		return post;
	}

	private String getResponseContent(HttpUriRequest request) {
		HttpResponse response = null;
		try {
			response = client.execute(request);
		} catch (IOException e) {
			logger.error("execute http request error", e);
		}
		if (response != null) {
			try {
				BufferedReader br = new BufferedReader(new InputStreamReader(
						response.getEntity().getContent()));
				String line = null;
				StringBuilder sb = new StringBuilder();
				while ((line = br.readLine()) != null) {
					sb.append(line);
				}
				br.close();
				String returnvalue = sb.toString();
				logger.info(returnvalue);
				return returnvalue;
			} catch (IllegalStateException | IOException e) {
				logger.error("Read response failed!", e);
				return null;
			}
		}
		return null;
	}

	/**
	 * The response is JSON-represented, like
	 * {"access_token":"1b883fef0f9102900b928c18978fed24"
	 * ,"expires_in":3600,"scope"
	 * :null,"refresh_token":"fa7b7a5d1638a038473b83951ab6f461"}, so parse it
	 * 
	 * @param response
	 * @return
	 */
	private Token parseResponse(String response) {
		JSONObject jo = JSONObject.parseObject(response);
		token.setAccess_token((String) jo.get("access_token"));
		token.setRefresh_token((String) jo.get("refresh_token"));
		token.setExpire_time((int) jo.get("expires_in"));

		token.saveToFile();
		return token;
	}

	private Token initToken() {
		String authCode = new Authenticator().getAuthCode();
		token.setAuthorizationCode(authCode);
		HttpPost post = buildGetTokenHTTPPost(authCode);
		String response = getResponseContent(post);
		parseResponse(response);
		return token;
	}

	private Token refreshToken() {
		synchronized (token) {
			HttpPost post = buildRefreshTokenHTTPPost(token.getRefresh_token());
			String response = getResponseContent(post);
			parseResponse(response);
		}
		return token;

	}

	private String getAccessToken_() {
		return token.getAccess_token();
	}

	public static String getAccessToken() {
		return THE_TOKEN_MANAGER.getAccessToken_();
	}
}
